/*
 * @(#)Xfsl.java        1.0 2000/05/09
 *
 * This file is part of Xfuzzy 3.0, a design environment for fuzzy logic
 * based systems.
 *
 * (c) 2000 IMSE-CNM. The authors may be contacted by the email address:
 *                    xfuzzy-team@imse.cnm.es
 *
 * Xfuzzy is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by
 * the Free Software Foundation.
 *
 * Xfuzzy is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 */

package xfuzzy.xfsl.view;

import xfuzzy.*;
import xfuzzy.lang.*;
import xfuzzy.util.*;
import xfuzzy.xfsl.model.XfslConfig;
import xfuzzy.xfsl.model.XfslProcess;
import xfuzzy.xfsl.model.XfslStatus;
import xfuzzy.xfsl.view.listener.*;

import javax.swing.*;
import java.awt.*;
import java.io.*;

/**
 * Herramienta de ajuste param�trico
 * 
 * @author Francisco Jos� Moreno Velo
 * 
 */
public class Xfsl extends JFrame implements IXfslFrame {

	// ----------------------------------------------------------------------------//
	// COSTANTES PRIVADAS //
	// ----------------------------------------------------------------------------//

	/**
	 * C�digo asociado a la clase serializable
	 */
	private static final long serialVersionUID = 95505666603063L;

	// ----------------------------------------------------------------------------//
	// MIEMBROS PRIVADOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Sistema difuso original
	 */
	private Specification original;

	/**
	 * Copia de trabajo del sistema difuso
	 */
	private Specification workingcopy;

	/**
	 * Estado del proceso de ajuste param�trico
	 */
	private int status = XfslStatus.UNCONFIGURED;

	/**
	 * Ventana principal del entorno
	 */
	private Xfuzzy xfuzzy;

	/**
	 * Configuraci�n del proceso de ajuste param�trico
	 */
	private XfslConfig config;

	/**
	 * Panel de configuraci�n
	 */
	private XfslConfigPanel configpanel;

	/**
	 * Panel de estado
	 */
	private XfslStatusPanel statuspanel;

	/**
	 * Panel de representaci�n gr�fica
	 */
	private XfslGraphPanel graphpanel;

	/**
	 * Barra de botones
	 */
	private XCommandForm form;

	/**
	 * Proceso de ajuste
	 */
	private XfslProcess process;

	/**
	 * Condicion de parada
	 */
	private boolean stop = false;

	// ----------------------------------------------------------------------------//
	// CONSTRUCTOR //
	// ----------------------------------------------------------------------------//

	/**
	 * Constructor
	 */
	public Xfsl(Xfuzzy xfuzzy, Specification spec) {
		super("Xfsl");
		this.xfuzzy = xfuzzy;
		this.original = spec;
		this.workingcopy = xfuzzy.duplicate(spec);
		build();
	}

	/**
	 * Constructor
	 * 
	 * @param spec
	 */
	public Xfsl(Specification spec) {
		super("Xfsl");
		this.xfuzzy = null;
		this.original = spec;
		this.workingcopy = spec;
		build();
	}

	// ----------------------------------------------------------------------------//
	// M�TODOS P�BLICOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Obtiene la especificaci�n del sistema
	 */
	public Specification getSpec() {
		return this.workingcopy;
	}

	/**
	 * Obtiene la configuraci�n del aprendizaje
	 */
	public XfslConfig getConfig() {
		return this.config;
	}

	/**
	 * Actualiza la configuraci�n del aprendizaje
	 */
	public void setConfig(XfslConfig config) {
		this.config = config;
	}

	/**
	 * Actualiza el estado ante un cambio de configuraci�n
	 */
	public void setStatus() {
		if (this.status == XfslStatus.UNCONFIGURED && this.config.isReadyToRun()) {
			this.status = XfslStatus.READY_TO_RUN;
			this.statuspanel.set(XfslStatus.READY_TO_RUN);
		}
		if (this.status == XfslStatus.READY_TO_RUN && !this.config.isReadyToRun()) {
			this.status = XfslStatus.UNCONFIGURED;
			this.statuspanel.set(XfslStatus.UNCONFIGURED);
		}
		if (this.config.isReadyToRun())
			setSensitive(XfslStatus.READY_TO_RUN);
		if (!this.config.isReadyToRun())
			setSensitive(XfslStatus.UNCONFIGURED);
		this.statuspanel.setTitles(this.config.errorfunction.isClassification());
		repaint();
	}

	/**
	 * Directorio de trabajo
	 */
	public File getWorkingDirectory() {
		return xfuzzy.getWorkingDirectory();
	}

	/**
	 * Mostrar mensajes
	 */
	public void log(String msg) {
		xfuzzy.log(msg);
	}

	/**
	 * Actualiza el estado del aprendizaje
	 */
	public void setStatus(XfslStatus slstat) {
		if (this.status != slstat.status)
			setSensitive(slstat.status);
		this.status = slstat.status;
		this.statuspanel.set(slstat);
		this.graphpanel.addStatus(slstat);
	}

	/**
	 * Accion asociada al boton Run
	 */
	public void run() {
		if (process == null || !process.isAlive()) {
			graphpanel.reset();
			process = new XfslProcess(this);
			this.stop = true;
			return;
		}
		if (process != null && process.isAlive() && stop) {
			process.sendStop();
			setSensitive(XfslStatus.STOPPING);
			this.stop = false;
			return;
		}
		if (process != null && process.isAlive() && !stop) {
			process.sendContinue();
			setSensitive(XfslStatus.LEARNING);
			this.stop = true;
			return;
		}
	}

	/**
	 * Accion asociada al boton Reload
	 */
	public void reload() {
		if (process != null) {
			if (process.isAlive()) {
				process.sendFinish();
				try {
					process.join();
				} catch (InterruptedException ex) {}
			}
			process = null;
		}
		statuspanel.reset(config.isReadyToRun());
		graphpanel.reset();
		if (config.algorithm != null)
			config.algorithm.reinit();
		workingcopy = xfuzzy.duplicate(original);
	}

	/**
	 * Accion asociada al boton Apply
	 */
	public void apply() {
		if (status == XfslStatus.LEARNING)
			return;
		Specification dup = xfuzzy.duplicate(workingcopy);
		original.setOperatorsets(dup.getOperatorsets());
		original.setTypes(dup.getTypes());
		original.setRulebases(dup.getRulebases());
		original.setSystemModule(dup.getSystemModule());
		original.setModified(true);
	}

	/**
	 * Accion asociada al boton Save
	 */
	public void save() {
		if (status == XfslStatus.LEARNING)
			return;
		File root = original.getFile();
		if (root == null)
			root = xfuzzy.getWorkingDirectory();
		XFileChooser chooser = new XFileChooser(root, XFileChooser.SAVE);
		int returnVal = chooser.showDialog();
		if (returnVal != XFileChooser.SELECTED)
			return;
		File file = chooser.getSelectedFile();
		workingcopy.save_as(file);
		xfuzzy.load(file);
	}

	/**
	 * Accion asociada al boton Close
	 */
	public void close() {
		if (process != null && process.isAlive()) {
			process.sendFinish();
			try {
				process.join();
			} catch (InterruptedException ex) {}
		}
		process = null;
		setVisible(false);
		if (xfuzzy == null)
			System.exit(0);
	}

	// ----------------------------------------------------------------------------//
	// M�TODOS PRIVADOS //
	// ----------------------------------------------------------------------------//

	/**
	 * Construye la ventana principal
	 */
	private void build() {

		String label[] = { "Run", "Reload", "Apply", "Save", "Close" };

		this.form = new XCommandForm(label, label, new XfslActionListener(this));
		this.form.setCommandWidth(120);
		this.form.block();
		this.config = new XfslConfig();
		this.configpanel = new XfslConfigPanel(this);
		this.statuspanel = new XfslStatusPanel();
		this.graphpanel = new XfslGraphPanel(this);

		JPanel box = new JPanel();
		box.setLayout(new BoxLayout(box, BoxLayout.X_AXIS));
		box.add(configpanel);
		box.add(statuspanel);
		Dimension maxsize = box.getMaximumSize();
		Dimension prefsize = box.getPreferredSize();
		box.setMaximumSize(new Dimension(maxsize.width, prefsize.height));

		Container content = getContentPane();
		content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));
		content.add(new XLabel("Supervised Learning for " + original));
		content.add(box);
		content.add(graphpanel);
		content.add(form);

		setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
		setIconImage(XfuzzyIcons.xfuzzy.getImage());
		addWindowListener(new XfslWindowListener(this));
		pack();

		Dimension size = getToolkit().getScreenSize();
		setLocation(size.width / 2 - getWidth() / 2, size.height / 2 - getHeight() / 2);

		this.configpanel.set();
	}

	/**
	 * (Des)Habilita los botones de la barra de comandos
	 */
	private void setSensitive(int status) {
		if (status == XfslStatus.UNCONFIGURED) {
			form.setLabel(0, "Run");
			form.setEnabled(0, false);
			form.setEnabled(1, true);
			form.setEnabled(2, true);
			form.setEnabled(3, true);
			form.setEnabled(4, true);
			configpanel.setEnabled(true);
			return;
		}
		if (status == XfslStatus.READY_TO_RUN) {
			form.setLabel(0, "Run");
			form.setEnabled(0, true);
			form.setEnabled(1, true);
			form.setEnabled(2, true);
			form.setEnabled(3, true);
			form.setEnabled(4, true);
			configpanel.setEnabled(true);
			return;
		}
		if (status == XfslStatus.LEARNING) {
			form.setLabel(0, "Stop");
			form.setEnabled(0, true);
			form.setEnabled(1, false);
			form.setEnabled(2, false);
			form.setEnabled(3, false);
			form.setEnabled(4, false);
			configpanel.setEnabled(false);
			return;
		}
		if (status == XfslStatus.STOPPING) {
			form.setLabel(0, "Stopping");
			form.setEnabled(0, false);
			form.setEnabled(1, false);
			form.setEnabled(2, false);
			form.setEnabled(3, false);
			form.setEnabled(4, false);
			configpanel.setEnabled(false);
			return;
		}
		if (status == XfslStatus.STOPPED) {
			form.setLabel(0, "Continue");
			form.setEnabled(0, true);
			form.setEnabled(1, true);
			form.setEnabled(2, true);
			form.setEnabled(3, true);
			form.setEnabled(4, true);
			configpanel.setEnabled(true);
			return;
		}
		if (status == XfslStatus.FINISHED) {
			form.setLabel(0, "Run");
			form.setEnabled(0, true);
			form.setEnabled(1, true);
			form.setEnabled(2, true);
			form.setEnabled(3, true);
			form.setEnabled(4, true);
			configpanel.setEnabled(true);
			return;
		}
	}
}
